using System;
using CSW.Framework.Common.Binding.Interfaces;
using CSW.Framework.Common.Binding.ValueTypes;

namespace CSW.Framework.Common.Binding
{
    /// <summary>
    /// BusinessObject base class.
    /// </summary>
    public abstract class BusinessObject : BindableObject, IBusinessObject
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="BusinessObject"/> class.
        /// </summary>
        protected BusinessObject()
        {
            //RefreshRequested += delegate { Refresh(); };
        }

        /// <summary>
        /// Tries to save the record to the database.
        /// </summary>
        /// <returns><c>true</c> if successful; otherwise, <c>false</c>.</returns>
        public bool TrySave()
        {
            string errorMessage;
            return TrySave(out errorMessage);
        }

        /// <summary>
        /// Tries to save the record to the database. It is this method's responsibility to <see cref="BindableObject.CheckAllRules(out string)"/> and set <see cref="BusinessObject.IsEditting"/> to false.
        /// </summary>
        /// <param name="errorMessage">The error message.</param>
        /// <returns><c>true</c> if successful; otherwise, <c>false</c>.</returns>
        public abstract bool TrySave(out string errorMessage);

        /// <summary>
        /// Saves the record to the database. If validation errors exist an exception 
        /// will be thrown. See <see cref="TrySave(out string)"/>.
        /// </summary>
        public virtual void Save()
        {
            string errorMessage;
            if (!TrySave(out errorMessage))
                throw new Exception(errorMessage);
        }

        /// <summary>
        /// Tries to save the detail records to the database.
        /// </summary>
        /// <param name="errorMessage">The error message.</param>
        /// <returns><c>true</c> if successful; otherwise, <c>false</c>.</returns>
        protected bool TrySaveDetails(out string errorMessage)
        {
            // Delete removed records
            foreach (IChildListProperty list in DetailLists)
            {
                if (!list.IsInitialized)
                    continue;

                if (!list.TrySave(out errorMessage))
                    return false;
            }

            errorMessage = null;
            return true;
        }

        /// <summary>
        /// Begins edit mode.
        /// </summary>
        public virtual void BeginEdit()
        {
            BeginEdit(true);
        }

        /// <summary>
        /// Begins edit mode.
        /// </summary>
        /// <param name="refresh"><c>true</c> to refresh the object from the database; <c>false</c> to leave in current state (default = true).</param>
        public virtual void BeginEdit(bool refresh)
        {
            if (IsEditting)
                throw new Exception(string.Format("'{0}' ({1}) is already in edit mode.", GetType().FullName, this));

            IsEditting = true;

            if (refresh)
                Refresh();

            /*if (IsNew)
                PopulateInitialValues();*/

            PopulateInitialValues();
        }

        /// <summary>
        /// Cancels edit mode.
        /// </summary>
        public virtual void CancelEdit()
        {
            if (!IsEditting)
                throw new Exception(string.Format("'{0}' ({1}) is not in edit mode.", GetType().FullName, this));

            IsEditting = false;

            if (ObjectState != BindableObjectState.Pristine)
                Refresh();
        }

        /// <summary>
        /// Throws an exception if the object is not edittable.
        /// </summary>
        protected override void CheckEdittable()
        {
            if (!IsLoading && !IsEditting)
            {
                BeginEdit(false);
                //throw new Exception(string.Format("'{0}' ({1}) is not in edit mode.", GetType().FullName, this));
            }
        }

        /// <summary>
        /// Gets a value indicating whether this instance is in edit mode.
        /// </summary>
        /// <value>
        /// 	<c>true</c> if this instance is in edit mode; otherwise, <c>false</c>.
        /// </value>
        public bool IsEditting
        {
            get;
            protected set;
        }

        /// <summary>
        /// Determines whether the object is new (not saved to the database).
        /// </summary>
        /// <returns><c>true</c> if this instance is new; otherwise, <c>false</c>.</returns>
        public abstract bool IsNew { get; }

        /// <summary>
        /// Refreshes this instance from the database. This method should follow the pattern: BeginLoading(); try { ... } finally { EndLoading(); }
        /// </summary>
        public abstract void Refresh();

        /// <summary>
        /// Deletes the record from the database.
        /// </summary>
        public abstract void Delete();
    }
}
